#include "softSerialReceiver.h"
#include <system.h>
#include <avr/io.h>
#include <avr/interrupt.h>

static uint8_t databitTime;
static uint8_t startbitTime;
static volatile uint8_t status;
static char data;
static uint8_t prescaler;
static RingBuffer buffer(128);

void timer0SoftSerialReceiver_init(uint32_t baudRate) {
	// Port DDR and external interrupts are not set up here. Up to caller to do that.
	// But we do set up the timer speed.
	// Assume we have to work with 38400 and 57600 baud (those are the speeds of GPS
	// and MAVLink interfaces). That is 208 clock cycles per bit (wow that is fast!).
	// We can assume timers run with 1:1 prescale.
	
	uint16_t time = F_CPU / baudRate;
	if (time > 255) {
		prescaler = (1<<CS01);
		time /= 8;
	} else {
		prescaler = (1<<CS00);
	}
	databitTime = time;
	time = time*3/2;
	if (time>255) time = 255;
	
	startbitTime = time; //200;

	// Set timer to CTC and enable interrupt (but do not start timer).
	TCCR0A = 1<<WGM01;
	TIMSK0 |= (1<<OCIE0A);
	
	TCCR0B = 0;
	
	// Set to falling edge interrupt.
	EICRA |= (1<<ISC01);
	EICRA &= ~(1<<ISC00);
	
	// Enable external interrupt, clearing the flag.
	EIFR |= (1<<INT0);
	EIMSK |= (1<<INT0);
}

SoftSerialReceiver::SoftSerialReceiver(uint32_t baudRate) {
	timer0SoftSerialReceiver_init(baudRate);
}

ISR(TIMER0_COMPA_vect) {
	if (status==SoftSerialReceiver::STOPBIT) {
		// Data goes to GPS.
		if (buffer.freeSpace()) {
			buffer.write(data);
		}
		status=SoftSerialReceiver::WAITSTART;
		// and stop the timer.
		TCCR0B = 0;
		// Enable external interrupt, clearing the flag.
		EIFR |= (1<<INT0);
		EIMSK |= (1<<INT0);
	} else {
		OCR0A = databitTime;
		uint8_t databit = PIND & (1<<2);
		data = data>>1;
		if (databit) {
			data |= 0x80;
		} else {
			data &= ~0x80;
		}
		status++;
	}
}

ISR(INT0_vect) {
	TCNT0 = 0;
	OCR0A = startbitTime;
	status = SoftSerialReceiver::DATA0;
	// Disable external interrupt.
	EIMSK &= ~(1<<INT0);
	// Start timer.
	TCCR0B = prescaler;
}

char SoftSerialReceiver::read() {
	return buffer.read();
}

void SoftSerialReceiver::clear() {
	buffer.clear();
}

size_t SoftSerialReceiver::available() {
	return buffer.available();
}
